package com.sghd.fight.module.game.service.match;

import com.sghd.common.utils.json.JsonUtils;
import com.sghd.common.utils.math.RandomUtils;
import com.sghd.fight.module.game.event.PushEvent;
import com.sghd.fight.module.game.service.GameService;
import com.sghd.fight.module.game.service.ThreadService;
import com.sghd.fight.module.game.service.configure.Configure;
import com.sghd.fight.module.game.service.room.IRoom;
import com.sghd.fight.module.game.service.room.Player;
import com.sghd.fight.module.game.service.room.PvpRoom;
import com.sghd.fight.module.game.service.work.RoomRunner;
import com.sghd.fight.module.game.service.work.RoomWork;
import com.sghd.fight.net.SocketEnum;
import com.sghd.pojo.fight.game.MatchResult;
import com.sghd.pojo.fight.game.PlayerVo;
import com.sghd.pojo.fight.game.Result;

import java.util.*;

/**
 * @author frank
 * @date 2018-10-25 10:41
 * @desc
 **/
public class PvpMatcher extends AbstractMathcer {

    /**  匹配成功的玩家*/
    private Map<Long, MatchResult> matchResults = new HashMap<>();
    private ThreadService threadService;
    private int joinRoobot = 4;
    private int readyTimeout = 10000;
    protected long roobotIdIndex;

    public PvpMatcher(GameService battleService, Configure configure, ThreadService threadService) {
        super(battleService, configure, 3000);
        timer(new Date());
        this.threadService = threadService;
        timeoutLisener(new Date(System.currentTimeMillis() + 500));
    }

    public void timeoutLisener(Date next) {
        threadService.add(new RoomWork(new RoomRunner() {
            @Override
            public void run(Date now) {
                timeout(now);
                timeoutLisener(new Date(next.getTime() + 500));
            }
        }, next, RoomWork.MATCH));
    }

    /**
     * 准备超时
     * */
    public void timeout(Date now) {
        long current = now.getTime();
        Set<Long> outed = new HashSet<>();
        for (MatchResult result : new HashSet<>(matchResults.values())) {
            //超时时间未到
            if (current < result.getEnd()) {
                continue;
            }
            //队列移除
            outed.addAll(result.getPlayerIds());
            PlayerVo left = result.getLeft();
            PlayerVo right = result.getRight();
            Set<Long> canceled = new HashSet<>();
            if (!left.isReady()) {
                logger.debug("玩家[{}]确认超时，移出队列！", left.getPlayerId());
                canceled.add(left.getPlayerId());
            }
            if (!right.isReady()) {
                logger.debug("玩家[{}]确认超时，移出队列！", right.getPlayerId());
                canceled.add(right.getPlayerId());
            }
            if (canceled.isEmpty()) {
                logger.debug("匹配成功的所有玩家已准备！");
                break;
            }
            //把已经准备好的玩家重新进入匹配
            if (!canceled.contains(left.getPlayerId()) && !left.isRoobot()) {
                add(left);
            }
            if (!canceled.contains(right.getPlayerId()) && !right.isRoobot()) {
                add(right);
            }
            gameService.post(PushEvent.NAME, PushEvent.valueOf(Result.valueOf(canceled), Arrays.asList(left.getPlayerId(), right.getPlayerId()), SocketEnum.CANCEL_MATCH));
        }
        for (Long remove : outed) {
            matchResults.remove(remove);
        }
    }

    @Override
    public List<IRoom> doMatch(Date now) {
        //匹配算法
        int roomSize = matchers.size() / roomCount;
        //参于匹配的玩家人数不足
        if (roomSize <= 0) {
            for (PlayerVo playerVo : matchers.values()) {
                //匹配次数加一
                playerVo.match();
                if (playerVo.getMatchTimes() > joinRoobot) {
                    //添加一个机器人
                    PlayerVo roobot = PlayerVo.valueOfRoobot(--roobotIdIndex, RandomUtils.nextInt(12));
                    matchers.put(roobot.getPlayerId(), roobot);
                    //继续匹配
                    doMatch(now);
                }
            }
            return null;
        }
        Set<PlayerVo> outed = new HashSet<>();
        long end = System.currentTimeMillis() + readyTimeout;
        while (roomSize-- > 0) {
            PlayerVo left = matched(Player.LEFT, outed);
            PlayerVo right = matched(Player.RIGHT, outed);
            MatchResult matched = MatchResult.valueOf(left, right, end);
            matchResults.put(left.getPlayerId(), matched);
            matchResults.put(right.getPlayerId(), matched);
            //推送
            gameService.post(PushEvent.NAME, PushEvent.valueOf(matched.toVo(), matched.getPlayerIds(), SocketEnum.MATCH_SUCCESS));
            //机器人自动准备
            if (left.isRoobot()) {
                robotReady(left);
            }
            if (right.isRoobot()) {
                robotReady(right);
            }
        }
        for (PlayerVo playerInfo : outed) {
            //移除匹配成功的玩家
            matchers.remove(playerInfo.getPlayerId());
        }
        return null;
    }

    @Override
    public boolean containsKey(long playerId) {
        return matchers.containsKey(playerId) || matchResults.containsKey(playerId);
    }

    @Override
    public void add(PlayerVo playerInfo) {
        playerInfo.setReady(false);
        super.add(playerInfo);
    }

    /**
     * 取消匹配
     * @param playerId 目标玩家
     */
    @Override
    public void quit(long playerId) {
        //推送目标
        Set<Long> pushed = new HashSet<>();
        //如果玩家已经匹配成功，则另一个玩家重新进入匹配
        if (matchResults.containsKey(playerId)) {
            MatchResult matchResult = matchResults.remove(playerId);
            PlayerVo other = matchResult.getOther(playerId);
            //把另一个玩家移出队列
            matchResults.remove(other.getPlayerId());
            pushed.add(playerId);
            pushed.add(other.getPlayerId());
            //另一个玩家，不是机器人就重新进入匹配队列
            if (!other.isRoobot()) {
                add(other);
            }
        }
        if (matchers.containsKey(playerId)) {
            matchers.remove(playerId);
            pushed.add(playerId);
        }
        if (!pushed.isEmpty()) {
            if (logger.isDebugEnabled()) {
                logger.debug("玩家[{}]取消匹配！", JsonUtils.object2String(pushed));
            }
            gameService.post(PushEvent.NAME, PushEvent.valueOf(Result.valueOf(Arrays.asList(playerId)), pushed, SocketEnum.CANCEL_MATCH));
        }
    }

    public void robotReady(PlayerVo robot) {
        gameService.addTimer(new RoomWork(new RoomRunner() {
            @Override
            public void run(Date now) {
                logger.debug("机器人[{}]确认进入游戏！", robot.getPlayerId());
                ready(robot.getPlayerId());
            }
        }, new Date(System.currentTimeMillis() + RandomUtils.betweenInt(2000, 6000, true)), RoomWork.MATCH));
    }

    /**
     * 确认进入
     * @param player
     */
    public void ready(long player) {
        if (!matchResults.containsKey(player)) {
            return;
        }
        MatchResult matchResult = matchResults.remove(player);
        matchResult.ready(player);
        Map<String, Object> body = new HashMap<>();
        body.put("id", player);
        gameService.post(PushEvent.NAME, PushEvent.valueOf(body, matchResult.getPlayerIds(), SocketEnum.MATCH_READY));
        if (!matchResult.canBegin()) {
            return;
        }
        IRoom room = configure.build(getRoomId(), gameService);
        ((PvpRoom) room).setScene(matchResult.getScene());
        Player red = null, blue = null;
        PlayerVo left = matchResult.getLeft();
        PlayerVo right = matchResult.getRight();
        Date now = new Date();
        if (left.isRoobot()) {
            red = configure.transformRobot(left, room, now);
        } else {
            red = configure.transform(left, room, now);
        }
        if (right.isRoobot()) {
            blue = configure.transformRobot(right, room, now);
        } else {
            blue = configure.transform(matchResult.getRight(), room, now);
        }
        room.join(red, blue);
        gameService.matched(Arrays.asList(room));
    }

}
